package com.livestocksolutions;

import java.util.HashMap;

import com.livestocksolutions.util.UpdateHistory;

/* -- Version History --
 *  1.0 : 27 December 2012 - Initial version.
 */

/**
 * This class identifies an UsableItem that will be used in a mixture.
 * @author Adam Parr
 * @version 1.0
 * 
 * @.modified 05 March 2013
 */
public class UsedItem {
	/**
	 * Keep track of any changes of this UsedItem.
	 */
	protected HashMap<String,UpdateHistory> update = new HashMap<String,UpdateHistory>();
	/**
	 * ID of the item that is used.
	 */
	protected long item;
	/**
	 * The amount of this item used, {@link #measurement} provides the scale
	 */
	protected double units;
	/**
	 * Units this item is measured in with {@link #units}
	 */
	protected String measurement;
	
	/**
	 * Default Constructor.
	 */
	public UsedItem(){}
	/**
	 * Create an UsedItem Object using the given UsableItem and ratio of the item.
	 * @param item The UsableItem that is being used within a mixture.
	 * @param units The amount of the item that is consumed for this UsedItem.
	 * @param measurement The unit of measure for the units provided.
	 */
	public UsedItem(UsableItem<?> item, double units, String measurement){
		setItem(item);
		setMeasurement(units,measurement);
	}
	
	/**
	 * Get the ID of this UsedItem.<br>
	 * It is assumed that any object containing multiple UsedItem objects
	 * will not need to contain multiple UsedItems with the same Item.
	 * @return A long integer consisting of the ID of the item used.
	 */
	public long getID(){
		return item;
	}
	
	/**
	 * Set the item of this UsedItem.
	 * @param item The UsableItem to set.
	 */
	public void setItem(UsableItem<?> item){
		this.item = item.id;
		update.put("ITEM", new UpdateHistory(String.valueOf(this.item)));
	}
	
	/**
	 * Set the units and measurement of the UsableItem used for the mixture.
	 * @param units The amount of the item that is consumed for this UsedItem.
	 * @param measurement The unit of measure for the units provided.
	 */
	public void setMeasurement(double units, String measurement){
		this.units = units;
		this.measurement = measurement;
		update.put("UNITS", new UpdateHistory(String.valueOf(units)));
		update.put("MEASUREMENT", new UpdateHistory(String.valueOf(measurement)));
	}
	
	/**
	 * Set the item of this UsedItem.
	 * @return The UsableItem used in the mixture.
	 */
	public UsableItem<?> getItem(){
		return LivestockSolutions.getUsableItemByID(item);
	}
	
	/**
	 * Get the units of the UsableItem used for the mixture.
	 * @return The amount of the item that is consumed for this UsedItem.
	 */
	public double getUnits(){
		return units;
	}
	
	/**
	 * Get the measurement of the units used for the mixture.
	 * @return The unit of measure for the units provided.
	 */
	public String getMeasurement(){
		return measurement;
	}
	
	/**
	 * Updates this Object with the updated values from the different version.
	 * @param changes UsedItem that represents this Object in a different version.
	 * @return <tt>true</tt> if changes is not null and changes does not equal this object, <tt>false</tt> otherwise.
	 */
	public boolean update(UsedItem changes){
		if(changes!=null && this!=changes){			
			if(updateBefore("ITEM",changes)){
				item=changes.item;
				update.put("ITEM", new UpdateHistory(String.valueOf(item)));
			}

			if(updateBefore("UNITS",changes)){
				units=changes.units;
				update.put("UNITS", new UpdateHistory(String.valueOf(units)));
			}
			
			if(updateBefore("MEASUREMENT",changes)){
				measurement=changes.measurement;
				update.put("MEASUREMENT", new UpdateHistory(String.valueOf(measurement)));
			}
			return true;
		}
		return false;
	}
	
	/**
	 * Checks to see if the modification date of the item at this position is before the changed value
	 * @param key The String key at which to compare the modification dates of the modified objects.
	 * @param changes The UsedItem that represents this UsedItem on another system.
	 * @return <tt>true</tt> if the old value was modified before the changed value. <tt>false</tt> otherwise.
	 */
	protected boolean updateBefore(String key,UsedItem changes){
		if(this.update.get(key)==null && changes.update.get(key)!=null){
			return true;
		}else if(changes.update.get(key)==null){
			return false;
		}
		return !this.update.get(key).getDate().after(changes.update.get(key).getDate());
	}
	
	/**
	 * Change the class of the UsedItem to a different sub class of UsedItem
	 * @param subClass The sub class of UsedItem to change this item to.
	 * @return An instance of the sub class provided with the same data from this UsedItem,
	 *         or this UsedItem if an exception were to occur while trying to create the Object.
	 */
	public UsedItem changeType(Class<? extends UsedItem> subClass){
		try {
			UsedItem sc = subClass.getDeclaredConstructor().newInstance();
			sc.update=update;
			sc.item=item;
			sc.units=units;
			sc.measurement=measurement;
			return sc;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return this;
	}
}
